package com.ideaaedi.logback.defender.util;

import com.ideaaedi.logback.defender.strategy.DefenderStrategy;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static com.ideaaedi.logback.defender.constant.DefenderConstant.DOT_SIGN;

/**
 * 脱敏工具类
 *
 * @author JustryDeng
 * @since 2021/7/20 23:08:24
 */
public class DefenderUtil {

    private static final ClassLoader CLASS_LOADER = DefenderUtil.class.getClassLoader();

    /**
     * 替换(即:脱敏)
     *
     * @param str
     *         要脱敏的字符串
     * @param strategy
     *         脱敏策略
     * @return 脱敏后的字符串
     */
    public static String doReplace(String str, DefenderStrategy strategy) {
        // 脱敏起始位置索引
        int startIndex = strategy.retainPrefixCount();
        if (str == null || str.length() <= startIndex) {
            return str;
        }
        int suffixCount = strategy.retainSuffixCount();
        int diffLength = str.length() - startIndex - suffixCount;
        // 脱敏结束位置索引
        int endIndex = diffLength <= 0 ? str.length() : str.length() - suffixCount;
        char replaceChar = strategy.replaceChar();
        String replacerStr = getReplacerString(replaceChar, endIndex - startIndex);
        return new StringBuilder(str).replace(startIndex, endIndex, replacerStr).toString();
    }

    /**
     * 拼接length个replacer
     *
     * @param replacer
     *         要拼接的字符
     * @param length
     *         要拼接的个数
     * @return 拼接后的字符串
     */
    public static String getReplacerString(char replacer, int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            sb.append(replacer);
        }
        return sb.toString();
    }

    /**
     * 根据source获取T实例
     *
     * @param source
     *            可为： 1、全类名（具备无参构造）
     *                  2、容器中的bean name
     * @param targetClazz
     *            要获取的实例所属(父)类
     * @param applicationContext
     *            上下文
     *
     * @return T实例
     * @throws ClassNotFoundException 加载类时
     * @throws IllegalAccessException 获取实例时
     * @throws InstantiationException 获取实例时
     */
    @SuppressWarnings("unchecked")
    public static <T> T checkAndGetInstance(String source, Class<T> targetClazz,
                                            ApplicationContext applicationContext)
            throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (StringUtils.isBlank(source)) {
            throw new IllegalArgumentException("Source[" + source + "] must not be blank.");
        }
        if (source.contains(DOT_SIGN)) {
            Class<?> clazz = CLASS_LOADER.loadClass(source);
            Assert.isAssignable(targetClazz, clazz, "Clazz[" + clazz + "] must be "
                    + targetClazz + "'s sub class.");
            return (T)clazz.newInstance();
        }
        Object obj = applicationContext.getBean(source);
        Assert.notNull(obj, "Cannot load spring-bean [" + source + "] from ApplicationContext.");
        Assert.isAssignable(targetClazz, obj.getClass(), "clazz[" + obj.getClass() + "] must be "
                + targetClazz + "'s sub class.");
        return (T)obj;
    }

    /**
     * 获取枚举实例
     *
     * @param fullClassName
     *            枚举类 全类名。 如: DefaultDesensitizationStrategyEnum.NAME中， DefaultDesensitizationStrategyEnum的全类名。
     * @param item
     *            具体枚举项。 如: DefaultDesensitizationStrategyEnum.NAME中， NAME就对应item
     *
     * @return 枚举实例
     * @throws ClassNotFoundException 加载类时
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static DefenderStrategy checkAndGetEnumInstance(String fullClassName, String item)
            throws ClassNotFoundException {
        if (StringUtils.isAnyBlank(fullClassName, item)) {
            throw new IllegalArgumentException("classLongName[" + fullClassName + "] and item[" + item + "] must not be blank.");
        }
        Class<?> clazz = CLASS_LOADER.loadClass(fullClassName);
        Assert.isTrue(clazz.isEnum(), "clazz[" + clazz + "] is not Enum.");
        Class<Enum> enumClazz = (Class<Enum>)clazz;
        Enum enumObj = Enum.valueOf(enumClazz, item);
        if (enumObj instanceof DefenderStrategy) {
            return (DefenderStrategy)enumObj;
        }
        throw new IllegalArgumentException("enum[" + enumClazz + "] must be implement DefenderStrategy.");
    }
    
    /**
     * 将策略平铺开
     *
     * @param strategies 待平铺的策略
     * @return  平铺开后的策略
     */
    public static Map<String, DefenderStrategy> unfold(Map<DefenderStrategy, Set<String>> strategies) {
        if (CollectionUtils.isEmpty(strategies)) {
            return null;
        }
        Map<String, DefenderStrategy> keyStrategyMap = new HashMap<>(16);
        DefenderStrategy strategy;
        Set<String> fieldSet;
        for (Map.Entry<DefenderStrategy, Set<String>> entry : strategies.entrySet()) {
            strategy = entry.getKey();
            fieldSet = entry.getValue();
            if (fieldSet == null || fieldSet.isEmpty()) {
                continue;
            }
            for (String desensitizationField : fieldSet) {
                keyStrategyMap.put(desensitizationField, strategy);
            }
        }
        return keyStrategyMap;
    }
}
